<#include "license.ftl">
<@license/>
<#assign object = doc.object>
package ${object.@package}.service.check;

import org.jetbrains.annotations.NotNull;
import java.sql.ResultSet;
import java.util.HashMap;
import java.util.Map;
import java.sql.SQLException;
import java.util.logging.Logger;
import java.io.PrintWriter;

import redora.db.Statement;
import ${object.@package}.model.fields.${object.@name}Fields;
import redora.exceptions.ConnectException;
import redora.exceptions.QueryException;

import static java.util.logging.Level.SEVERE;
import static redora.service.ServiceBase.close;

/**
* Handler for the model vs database synchronization. It can create the ${object.@name} table if it does exists
* and it can check if the database definition is still in sync with your model with the check() function.
* For your convenience is can also drop the ${object.@name} table.
* @author Redora (www.redora.net)
*/
public class ${object.@name}TableCheck {
    private static final transient Logger l = Logger.getLogger("${object.@package}.service.check.${object.@name}TableCheck");
    private final Statement st;

    public ${object.@name}TableCheck(Statement st) {
        this.st = st;
    }

    /**
    * Checks if the model is the same with the database table, logs warning level message
    * if this is not the case.
    * @param out HTML stream with check results. If the check results OK, nothing is streamed
    */
    public void check(@NotNull PrintWriter out) throws ConnectException, QueryException {
        ResultSet rs = null;
        try {
            rs = st.con.con.getMetaData().getColumns("", "", "${object.@name}", "");
            Map<String, Map<String, Object>> map = new HashMap<String, Map<String, Object>>();
            while (rs.next()) {
                Map<String, Object> list = new HashMap<String, Object>();
                list.put("COLUMN_NAME", rs.getString("COLUMN_NAME"));
                list.put("DATA_TYPE", rs.getInt("DATA_TYPE"));
                list.put("COLUMN_SIZE", rs.getInt("COLUMN_SIZE"));
                list.put("IS_NULLABLE", rs.getString("IS_NULLABLE"));
                map.put(rs.getString("COLUMN_NAME"), list);
            }
            for (String key : map.keySet()) {
                boolean check = false;
                for (${object.@name?cap_first}Fields fields : ${object.@name?cap_first}Fields.values()) {
                    if (map.get(key).get("COLUMN_NAME").toString().equalsIgnoreCase(fields.name())) {
                        check = true;
                        break;
                    }
<#if object.trashcan == "true">
                    if (map.get(key).get("COLUMN_NAME").toString().equalsIgnoreCase("roDeleted")) {
                        check = true;
                        break;
                    }
</#if>
                }
                if (!check) {
                    out.print("${object.@name} does not have " + key + " attribute but it exists in database.<br>");
                }
            }
            for (${object.@name?cap_first}Fields fields : ${object.@name?cap_first}Fields.values()) {
                if (fields.name().equalsIgnoreCase("creationDate")) {
                    fields.sqlType = java.sql.Types.TIMESTAMP;
                }
                if (fields.name().equalsIgnoreCase("updateDate")) {
                    fields.sqlType = java.sql.Types.TIMESTAMP;
                }
<#list object.attributes?children as att>
    <#if att?node_name == "datetime">
                if (fields.name().equalsIgnoreCase("${att.@name}")) {
                    fields.sqlType = java.sql.Types.TIMESTAMP;
                }
    </#if>
</#list>
                if (fields.sqlType == java.sql.Types.BOOLEAN) {
                    fields.sqlType = java.sql.Types.BIT;
                }
                if (map.containsKey(fields.name()) && Integer.parseInt(map.get(fields.name()).get("DATA_TYPE").toString()) == fields.sqlType) {
<#list object.attributes?children as att>
    <#if att?node_name != "set" && att?node_type == "element">
                    if (fields.name().equalsIgnoreCase("${att.@name[0]!att.@class}")) {
        <#if att.@notnull[0]?? && att.@notnull[0] == "true">
                        if (!map.get(fields.name()).get("IS_NULLABLE").toString().equalsIgnoreCase("NO")) {
                            out.print("${object.@name}." + fields.name()+ " can't have null values.<br>");
                        }
        <#else>
                        if (!map.get(fields.name()).get("IS_NULLABLE").toString().equalsIgnoreCase("YES")) {
                            out.print("${object.@name}." + fields.name()+ " can have null values.<br>");
                        }
        </#if>
        <#if att?node_name=="string" && att.@maxlength[0]??>
                        if (Integer.parseInt(map.get(fields.name()).get("COLUMN_SIZE").toString()) != <#if att.@maxlength[0]?number &gt; 65000><#if att.@maxlength[0]?number &gt; 16000000>2147483647<#else>16777215</#if><#else>${att.@maxlength[0]}</#if>) {
                            out.print("${object.@name}." + fields.name()+ "'s maxlength do not match the database's.<br>");
                        }
        </#if>
        <#if att?node_name=="enum">
            <#assign enumValue = "enum(">
            <#list att.element as value>
                <#assign enumValue = enumValue + "'" + "${value.@name}" + "'">
                <#if value_has_next>
                    <#assign enumValue = enumValue + "," >
                </#if>
            </#list>
            <#assign enumValue = enumValue + ")">
                        String enumValue = "";
                        rs = st.st.executeQuery("show columns from `${object.@name}` like '${att.@class?uncap_first}'");
                        while (rs.next()) {
                            enumValue = rs.getString("Type");
                        }
                        if (!enumValue.equals("${enumValue}")) {
                            out.print("${object.@name}." + fields.name()+ "'s value ${enumValue} do not match the database's value " + enumValue + ".<br>");
                        }
        </#if>
                    }
    </#if>
</#list>
                } else {
                    out.print("${object.@name}." + fields.name() + " does not exist or its sqltype does not match according to xml.<br>");
                }
            }
        } catch (SQLException e) {
            l.log(SEVERE, "Failed to execute check ${object.@name}", e);
            throw new QueryException("failed to execute check ${object.@name?cap_first}", e);
        } finally {
            close(rs);
        }
    }
}
